/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2021-2023. All rights reserved.
 *
 */

import fse from 'fs-extra';
import os from 'os';
import path from 'path';

import { DefaultTargetConst, NativeConst } from '../const/common-const.js';
import { ExternalNativeOpt } from '../options/build/build-opt.js';
import { DefaultModuleDependency } from '../project/dependency/core/default-module-dependency.js';
import { Dependency } from '../project/dependency/core/dependency-interface.js';
import { CmakeUtil } from '../utils/cmake-util.js';
import { OhosLogger } from '../utils/log/ohos-logger.js';
import { PackageResolver } from '../utils/native-cmake-resolver.js';

import { TaskNames } from './common/task-names.js';
import { LegacyPreBuild } from './legacy-tasks/legacy-pre-build.js';
import { TargetTaskService } from './service/target-task-service.js';
import { OhosHapTask } from './task/ohos-hap-task.js';
import { PreBuild } from './pre-build.js';

const _log = OhosLogger.getLogger('ConfigureCmake');

/**
 * 根据已安装依赖中是否存在native产物（libs/include）
 * 为依赖生成cmake find脚本(exampleConfig.cmake)
 * 并将find脚本路径传递给cmake, 方便开发者引用三方so及接口
 *
 * @since 2024/01/02
 */
export class ConfigureCmake extends OhosHapTask {
  protected readonly nativeOption?: ExternalNativeOpt;
  protected readonly dependencies: Dependency[] = [];
  protected readonly cmakeDir: string;
  protected readonly abiList: string[];
  protected readonly packages: PackageResolver[] = [];

  constructor(targetService: TargetTaskService) {
    super(targetService, TaskNames.Task.CONFIGURE_CMAKE);
    this.nativeOption = targetService.getBuildOption().externalNativeOptions;
    this.abiList = CmakeUtil.checkAbiFilters(this.nativeOption?.abiFilters, targetService.getTargetData().isHarmonyOS(), this.moduleModel, this.targetName);
    this.cmakeDir = path.resolve(this.pathInfo.getNinjaWorkDir(), 'hvigor');
    this.init();
  }

  taskShouldDo(): boolean {
    return CmakeUtil.nativeTaskCondition(this.moduleModel, this.targetData, this.nativeOption);
  }

  init() {
    for (const dependency of this.service.getDependencyInfo().getNpmDependencies()) {
      const service = this.isLocal(dependency);
      if (!this.isNativeLibrary(dependency, service)) {
        continue;
      }
      _log.debug(`Resolve native package ${dependency.getDependencyName()}.`);
      this.dependencies.push(dependency);
      this.packages.push(PackageResolver.getInstance(dependency, this.abiList, this.cmakeDir, service));
    }
  }

  protected async doTaskAction() {
    await Promise.all(this.packages.map((pkg) => pkg.build()));

    await this.genCmakeFindFile();
  }

  async genCmakeFindFile() {
    for (const abi of this.abiList) {
      const summary = this.packages
        .map((pkg) => pkg.archs.find((arch) => arch.arch === abi))
        .filter((arch) => arch !== undefined)
        .map((arch) => {
          return arch
            ? `if(EXISTS ${arch.cmakeFile.replace(/\\/g, '/')})` + `${os.EOL}    find_package(${arch.pkg.name} REQUIRED CONFIG)` + `${os.EOL}endif()${os.EOL}`
            : '';
        })
        .join(os.EOL);

      const summaryFile = path.resolve(this.cmakeDir, abi, NativeConst.PACKAGE_SUMMARY_FILE);
      if (fse.existsSync(summaryFile)) {
        const originalText = await fse.readFile(summaryFile, { encoding: 'utf8' });
        if (originalText === summary) {
          return;
        }
      }
      await fse.ensureDir(path.dirname(summaryFile));
      await fse.writeFile(summaryFile, summary);
    }
  }

  public initTaskDepends(): void {
    this.declareDepends(this.isFaMode ? LegacyPreBuild.name : PreBuild.name);
    const service = this.targetService.getModuleService();
    for (const moduleName of service.getDependencyInfo().getModuleDependencyMap().keys()) {
      const service = this.getModuleTarget(moduleName);
      const target = service?.getTargetData().getTargetName() ?? 'default';
      this.declareDepends(`${target}@${TaskNames.Task.BUILD_NATIVE_WITH_CMAKE.name}`, moduleName);
    }
  }

  private isLocal(dependency: Dependency) {
    if (!dependency.isLocal()) {
      return undefined;
    }
    const moduleDep = this.service.getModuleDependencies().find((dep) => path.resolve(dep.getDependencyRootPath()) === dependency.getDependencyRootPath());
    if (!moduleDep) {
      return undefined;
    }
    return this.getModuleTarget((moduleDep as DefaultModuleDependency).getModuleName());
  }

  private getModuleTarget(moduleName: string) {
    const projectModel = this.targetService.getModuleService().getProjectModel();
    return (
      projectModel.getTarget(moduleName, this.targetName) ??
      projectModel.getTarget(moduleName, DefaultTargetConst.DEFAULT_TARGET) ??
      projectModel.getTarget(moduleName)
    );
  }

  private isNativeLibrary(dependency: Dependency, service?: TargetTaskService) {
    return service
      ? CmakeUtil.nativeTaskCondition(service.getModuleService().getModuleModel(), service.getTargetData(), service.getBuildOption().externalNativeOptions)
      : (fse.existsSync(path.resolve(dependency.getDependencyRootPath(), 'libs')) &&
          fse.readdirSync(path.resolve(dependency.getDependencyRootPath(), 'libs')).length > 0) ||
          fse.existsSync(path.resolve(dependency.getDependencyRootPath(), 'include'));
  }
}
